unit uSilhouette;

interface

uses
  System.Classes,

  GLS.VectorLists,
  GLS.VectorFileObjects,
  GLS.VectorTypes,
  GLS.VectorGeometry;

type
  TGLCIFace = class
    VertexIndex : array[0..2] of word;
    Visible : boolean;
  end;

  TGLCIEdge = class
    VertexIndex : array[0..1] of word;
    Triangle : array[0..1] of TGLCIFace;
  end;

  TGLConnectivityInformation = class
  private
    FTriangleList : TList;
    FEdgeList : TList;

    FMeshObject : TGLMeshObject;

    procedure AddEdge(VertexIndex0, VertexIndex1 : word; Triangle : TGLCIFace);

  public
    procedure Clear;
    procedure BuildEdgeList;

    procedure CreateSilhouette(SeenFrom : TAffineVector; List : TList);

    constructor Create(aMeshObject : TGLMeshObject);
    destructor Destroy; override;
  end;


implementation

{ TGLConnectivityInformation }

procedure TGLConnectivityInformation.AddEdge(VertexIndex0,
  VertexIndex1: word; Triangle: TGLCIFace);
var
  i : integer;
  Edge : TGLCIEdge;
  Vi0, Vi1 : word;
begin
  // Resort the indexes so we only need to search once for both combinations
  if VertexIndex0<=VertexIndex1 then
  begin
    Vi0 := VertexIndex0;
    Vi1 := VertexIndex1;
  end else
  begin
    Vi0 := VertexIndex1;
    Vi1 := VertexIndex0;
  end;//}

  for i := 0 to FEdgeList.Count-1 do
  begin
    // Retrieve an edge for testing
    Edge := TGLCIEdge(FEdgeList[i]);

    // If the edges connect the same two vertices as the edge we're creating,
    // then they are the same edge
    if (Edge.VertexIndex[0]=Vi0) and
       (Edge.VertexIndex[1]=Vi1) then
    begin
      // Update the second triangle of the edge and we're done (this _MAY_
      // overwrite a previous triangle in a broken mesh)
      Edge.Triangle[1] := Triangle;
      exit;
    end;
  end;

  // No edge was found, create a new one
  Edge := TGLCIEdge.Create;
  Edge.VertexIndex[0] := Vi0;
  Edge.VertexIndex[1] := Vi1;

  Edge.Triangle[0] := Triangle;

  FEdgeList.Add(Edge);
end;

procedure TGLConnectivityInformation.BuildEdgeList;
var
  iFaceGroup, iFace  : integer;
  FaceGroup : TFGVertexIndexList;
  Triangle : TGLCIFace;
  Vi0, Vi1, Vi2 : integer;
begin
  // Make sure that the connectivity information is empty
  Clear;

  // Create a list of edges for the meshobject
  for iFaceGroup := 0 to FMeshObject.FaceGroups.Count-1 do
  begin
    Assert(FMeshObject.FaceGroups[iFaceGroup] is TFGVertexIndexList,'Method only works for descendants of TFGVertexIndexList.');
    FaceGroup := TFGVertexIndexList(FMeshObject.FaceGroups[iFaceGroup]);

    for iFace := 0 to FaceGroup.TriangleCount - 1 do
    begin
      if FaceGroup.Mode = fgmmTriangles then
      begin
        Vi0 := FaceGroup.VertexIndices[iFace * 3 + 0];
        Vi1 := FaceGroup.VertexIndices[iFace * 3 + 1];
        Vi2 := FaceGroup.VertexIndices[iFace * 3 + 2];
      end else
        Assert(false, 'Currently only support for fgmmTriangles.');

      Triangle := TGLCIFace.Create;
      Triangle.VertexIndex[0] := Vi0;
      Triangle.VertexIndex[1] := Vi1;
      Triangle.VertexIndex[2] := Vi2;

      FTriangleList.Add(Triangle);

      AddEdge(Vi0, Vi1, Triangle);
      AddEdge(Vi1, Vi2, Triangle);
      AddEdge(Vi2, Vi0, Triangle);
    end;
  end;

  // If there are edges that belong to only one triangle, that means that the
  // mesh has holes in it. However, removing these orphaned edges would cause
  // severe problems with edges.
  {for iEdge := FEdgeList.Count-1 downto 0 do
  begin
    Edge := TGLCIEdge(FEdgeList[iEdge]);
    if Edge.Triangle[1]=nil then
    begin
      AddMessage(Format('Deleted orphaned edge : %d-%d',[Edge.VertexIndex[0], Edge.VertexIndex[1]]));
      FEdgeList.Delete(iEdge);
    end;
  end;//}
end;

procedure TGLConnectivityInformation.Clear;
var
  i : integer;
begin
  for i := 0 to FEdgeList.Count-1 do
    TGLCIEdge(FEdgeList[i]).Free;

  FEdgeList.Clear;

  for i := 0 to FTriangleList.Count-1 do
    TGLCIFace(FTriangleList[i]).Free;

  FTriangleList.Clear;
end;

constructor TGLConnectivityInformation.Create(aMeshObject : TGLMeshObject);
begin
  FTriangleList := TList.Create;
  FEdgeList := TList.Create;

  FMeshObject := aMeshObject;
end;

procedure TGLConnectivityInformation.CreateSilhouette(
  SeenFrom: TAffineVector; List: TList);
var
  i : integer;
  Triangle:TGLCIFace;
  Edge : TGLCIEdge;
  GLBaseMesh : TGLBaseMesh;
  V0, V1, V2 : TAffineVector;
  FaceNormal, LightNormal : TAffineVector;

  dot : single;
begin
  if List=nil then
    List:=TList.Create;

  List.Clear;

  GLBaseMesh := FMeshObject.Owner.Owner;

  SeenFrom := GLBaseMesh.AbsoluteToLocal(SeenFrom);

  // Update visibility information for all triangles
  for i := 0 to FTriangleList.Count-1 do
  begin
    Triangle := TGLCIFace(FTriangleList[i]);

    // Retrieve the actual vertices, in absolute coords
    V0 := FMeshObject.Vertices[Triangle.VertexIndex[0]];
    V1 := FMeshObject.Vertices[Triangle.VertexIndex[1]];
    V2 := FMeshObject.Vertices[Triangle.VertexIndex[2]];

    FaceNormal :=
      CalcPlaneNormal(V0, V1, V2);//}

    LightNormal :=
      VectorSubtract(
        SeenFrom, V0);//}

    dot := VectorDotProduct(FaceNormal, LightNormal);

    Triangle.Visible := (dot>=0);
  end;

  for i := 0 to FEdgeList.Count-1 do
  begin
    Edge := TGLCIEdge(FEdgeList[i]);

    if (Edge.Triangle[1]=nil) or
       (Edge.Triangle[0].Visible <> Edge.Triangle[1].Visible) then
      List.Add(Edge);
  end;
end;

destructor TGLConnectivityInformation.Destroy;
begin
  Clear;

  FTriangleList.Free;
  FEdgeList.Free;

  inherited;
end;
end.
